﻿// Inner Fire 游戏引擎库
// 纹理类
//
// Copyright (c) 2023 by 尔西大帝. All rights reserved.
//
// 用于纹理
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2024-12-01

#include "dx_texture.h"

#include <DDSTextureLoader.h>
#include <dx/core/dx_exception.h>
#include <enum_parser.h>
#include <image.h>
#include <kfile.h>
#include <xml_reader.h>

using namespace ikit;

namespace ifire::dx {
///////////////////////////////////////////////////////////////////////////////
// Texture
///////////////////////////////////////////////////////////////////////////////

DxTexture::DxTexture(int index, ikit::sstr name, TextureType type)
    : DxElement(index, name), type_(type) {}

void DxTexture::CreateFromFile(ID3D12Device* device, //
    ID3D12GraphicsCommandList* cmd, const std::string_view& filename, bool is_flip) {
  auto dd = to_wide(filename);
  // 检查是否是dds文件。否则使用DDS载入
  if (is_file_ext(filename, "dds")) {
    DirectX::CreateDDSTextureFromFile12(
        device, cmd, to_wide(filename).c_str(), resource_, uploader_);
    return;
  }

  auto img = ikit::load_image(to_wide(filename), is_flip);
  img.ConvertToChannels4(type_ == TextureType::Normal);

  CreateFromImage(device, cmd, img);

  // 删除缓存
  img.Release();
}

void DxTexture::CreateFromImage(ID3D12Device* device, //
    ID3D12GraphicsCommandList* cmd, const ImageData& img) {
  // TODO：这里要处理载入img失败的项目。如果载入失败，会导致空img，资源无法生成。

  // 使用纹理描述符
  D3D12_RESOURCE_DESC textureDesc = {};
  textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
  textureDesc.Width = img.width;
  textureDesc.Height = img.height;
  textureDesc.DepthOrArraySize = 1;
  textureDesc.MipLevels = 1;
  // 我看DDS载入NORMAL是这个格式，RGB是反过来。但是好像没什么用。
  if (type_ == TextureType::Normal) {
    // DDS 是DXGI_FORMAT_B8G8R8A8_UNORM
    textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
  } else {
    textureDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
  }
  textureDesc.SampleDesc.Count = 1; // 设置为1表示没有MSAA
  textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
  textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE;

  // 在堆中创建一个资源。这里直接就设置为COPY_DEST。
  auto props = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT);
  HASSERT(device->CreateCommittedResource(&props, D3D12_HEAP_FLAG_NONE,
      &textureDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr,
      IID_PPV_ARGS(&resource_)));

  // 创建一个上传堆
  UINT64 requiredSize = GetRequiredIntermediateSize(resource_.Get(), 0, 1);
  auto upload_props = CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD);
  auto upload_buffer = CD3DX12_RESOURCE_DESC::Buffer(requiredSize);
  HASSERT(device->CreateCommittedResource(&upload_props, D3D12_HEAP_FLAG_NONE,
      &upload_buffer, D3D12_RESOURCE_STATE_GENERIC_READ, nullptr,
      IID_PPV_ARGS(&uploader_)));

  // 使用上传中介上传资源
  D3D12_SUBRESOURCE_DATA subresourceData = {};
  subresourceData.pData = img.data;
  subresourceData.RowPitch = img.width * 4; // 每行的字节数
  subresourceData.SlicePitch = subresourceData.RowPitch * img.height;
  UpdateSubresources<1>(
      cmd, resource_.Get(), uploader_.Get(), 0, 0, 1, &subresourceData);

  // 将纹理资源转化为ShaderResource。
  D3D12_RESOURCE_BARRIER to_shader = CD3DX12_RESOURCE_BARRIER::Transition(
      resource_.Get(), D3D12_RESOURCE_STATE_COPY_DEST,
      D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
  cmd->ResourceBarrier(1, &to_shader);
}

TextureType DxTexture::ReadType(ikit::sstr name) {

  static std::pair<ikit::sstr, TextureType> enums[] = {
      {"Image", TextureType::Image},              //
      {"Normal", TextureType::Normal},            //
      {"Cube", TextureType::Cube},                //
      {"TextureArray", TextureType::TextureArray} //
  };
  static EnumParser<TextureType> typeParser(enums, (int)TextureType::Count);

  return typeParser.Get(name, TextureType::Image);
}

} // namespace ifire::dx
